package wjdlogger

import (
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"regexp"
	"sync"
	"time"
)

type WjdLogger struct {
	fileName string
	file     *os.File
}

type LogContent struct {
	fileName string
	content  string
}

var (
	WjdLoggerPool sync.Map
	fileNames     sync.Map
	logsChan      chan *LogContent
)

const (
	maxLogConc   = 100
	daysToDelete = 3
	timeDay      = 24 * time.Hour
)

func init() {
	logsChan = make(chan *LogContent, maxLogConc)
	go func() {
		defer func() {
			//关闭文件
			WjdLoggerPool.Range(func(fileName, wjdLogger interface{}) bool {
				wjdLogger.(WjdLogger).file.Close()
				return true
			})
		}()
		for {
			select {
			//写入文件
			case lc, ok := <-logsChan:
				if !ok {
					return
				}
				var (
					wjdLogger *WjdLogger
					nowTime   time.Time
				)
				nowTime = time.Now()
				fn := fmt.Sprintf("%s_%s.log", lc.fileName, nowTime.Format("20060102"))
				// 是否已打开
				if _, ok := fileNames.LoadOrStore(fn, struct{}{}); !ok {
					fd, err := os.OpenFile(fn, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0660)
					if err != nil {
						log.Panicf("open file error:%s", err)
					}
					wjdLogger = &WjdLogger{fileName: fn, file: fd}
					WjdLoggerPool.Store(fn, wjdLogger)
				} else {
					wjdLoggerI, _ := WjdLoggerPool.Load(fn)
					wjdLogger = wjdLoggerI.(*WjdLogger)
				}
				wjdLogger.file.WriteString(lc.content)
				wjdLogger.file.Sync()
			}
		}
	}()

	go func() {
		deleteExpiredLogs()
		tk := time.NewTicker(timeDay)
		for {
			select {
			case <-tk.C:
				deleteExpiredLogs()
			}
		}
	}()

}

func Log(fileName, content string) {
	// 异步写文件
	logsChan <- &LogContent{fileName: fileName, content: content}
}

func deleteExpiredLogs() {
	//删除文件
	nowTime := time.Now()
	today := time.Date(nowTime.Year(), nowTime.Month(), nowTime.Day(),
		0, 0, 0, 0, time.Local)
	dir, err := ioutil.ReadDir(".")
	if err != nil {
		log.Println("read dir error", err)
		return
	}
	regPattern := "_(\\d{8}).log"
	reg := regexp.MustCompile(regPattern)
	for _, f := range dir {
		subStrs := reg.FindStringSubmatch(f.Name())
		if subStrs != nil && len(subStrs) > 1 {
			dt, err := time.Parse("20060102", subStrs[1])
			if err != nil {
				log.Println("time parse error", err)
				continue
			}
			if today.Sub(dt) > daysToDelete*timeDay {
				os.Remove(f.Name())
			}
		}
	}
}
